package com.liuxing.javaSpringBoot.modules.common.controller;

import com.liuxing.javaSpringBoot.modules.common.entity.ExceptionLog;
import com.liuxing.javaSpringBoot.modules.common.service.ExceptionLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by IntelliJ IDEA.
 * @File ExceptionHandleController.java
 * @Auth liuxing
 * @Date 2021/8/25 09:23
 * @Email liuxing997@foxmail.com
 */
@ControllerAdvice
public class ExceptionHandleController {

    private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionHandleController.class);

    @Autowired
    private ExceptionLogService exceptionLogService;

    //捕捉算术异常
    @ExceptionHandler(value = {Exception.class})
    public ModelAndView ExceptionHandle(HttpServletRequest request, Exception e) {
        LOGGER.debug("====== Catch Exception ======");
        LOGGER.debug(e.getMessage());
        //根据异常的类型确定状态值和错误路径 --- 403 500
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        String errorPath = "/common/500";
        String message = "Internal Server Error.";
        //先假定ArithmeticException，没有权限异常
        if (e instanceof UnauthorizedException) {
            httpStatus = HttpStatus.FORBIDDEN;
            errorPath = "/common/403";
            message = "No permission.";
        }
        //保存异常信息
        saveExceptionLog(request, e);
        //判断访问的是view 还是数据接口
        //如果是 view 则返回对应的错误页面，如果是数据接口，则返回 result 接口数据
        if (isInterface(request)) {
            //返回json数据
            return returnJson(errorPath, httpStatus, message);
        } else {
            //返回错误页面
            return returnView(errorPath, httpStatus);
        }
    }

    //返回json数据
    private ModelAndView returnJson(String path, HttpStatus httpStatus, String message) {
        ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
        modelAndView.addObject("status", httpStatus.value());
        modelAndView.addObject("message", message);
        modelAndView.addObject("data", path);
        return modelAndView;
    }

    //返回错误页面
    private ModelAndView returnView(String path, HttpStatus httpStatus) {
        return new ModelAndView("redirect:" + path, httpStatus);
    }


    //判断是否为接口
    private boolean isInterface(HttpServletRequest request) {
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        if (handlerMethod == null) {
            return false;
        }
        ResponseBody[] methodAnnotation = handlerMethod.getMethod()
                .getAnnotationsByType(ResponseBody.class);
        ResponseBody[] typeAnnotation1 = handlerMethod.getBeanType()
                .getAnnotationsByType(ResponseBody.class);
        ResponseBody[] typeAnnotation2 = handlerMethod.getBeanType()
                .getAnnotationsByType(ResponseBody.class);
        return methodAnnotation.length > 0 ||
                typeAnnotation1.length > 0 ||
                typeAnnotation2.length > 0;
    }

    //保存异常信息
    private void saveExceptionLog(HttpServletRequest request, Exception e) {
        ExceptionLog exceptionLog = new ExceptionLog();
        exceptionLog.setIp(request.getRemoteAddr());
        exceptionLog.setPath(request.getRequestURI());
        exceptionLog.setClassName("");
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        if (handlerMethod != null) {
            exceptionLog.setClassName(handlerMethod.getBeanType().getName());
            exceptionLog.setMethodName(handlerMethod.getMethod().getName());
        }
        exceptionLog.setExceptionType(e.getClass().getSimpleName());
        exceptionLog.setExceptionMessage(StringUtils.isBlank(e.getMessage()) ? "" :
                e.getMessage().substring(0,
                        e.getMessage().length() > 200 ? 200:e.getMessage().length()));
        //保存
        exceptionLogService.insertExceptionLog(exceptionLog);
    }
}
